package com.lineengine.lineengine.utils;

import cn.hutool.core.util.StrUtil;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.function.BinaryOperator;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * <p>
 * Common Util
 * </p >
 *
 * @author wenrong.yu
 * @since 2020-11-30 15:55
 */

public final class CommonUtil {
    private CommonUtil() {
        throw new UnsupportedOperationException();
    }


    /**
     * 以-为分隔生成组合key
     *
     * @param args 参数
     * @return 组合key string
     */
    public static String join(String... args) {
        return String.join(StrUtil.UNDERLINE, args);
    }


    /**
     * 排除
     *
     * @param <E>       the type parameter
     * @param eList     the e list
     * @param predicate the predicate
     * @return list list
     */
    public static <E> List<E> filter(Collection<E> eList, Predicate<E> predicate) {
        if (CollectionUtils.isEmpty(eList)) {
            return new ArrayList<>();
        }
        return eList.stream().filter(predicate).collect(Collectors.toList());
    }

    /**
     * Filter collect set set.
     *
     * @param <E>       the type parameter
     * @param <R>       the type parameter
     * @param eList     the e list
     * @param predicate the predicate
     * @param mapper    the mapper
     * @return the set
     */
    public static <E, R> Set<R> filterCollectSet(Collection<E> eList, Predicate<E> predicate, Function<E, R> mapper) {
        if (CollectionUtils.isEmpty(eList)) {
            return new HashSet<>();
        }
        return eList.stream().filter(predicate).map(mapper).collect(Collectors.toSet());
    }

    /**
     * Filter collect list.
     *
     * @param <E>       the type parameter
     * @param <R>       the type parameter
     * @param eList     the e list
     * @param predicate the predicate
     * @param mapper    the mapper
     * @return the list
     */
    public static <E, R> List<R> filterCollect(Collection<E> eList, Predicate<E> predicate, Function<E, R> mapper) {
        if (CollectionUtils.isEmpty(eList)) {
            return new ArrayList<>();
        }
        return eList.stream().filter(predicate).map(mapper).collect(Collectors.toList());
    }

    /**
     * Collect list.
     *
     * @param <E>    the type parameter
     * @param <R>    the type parameter
     * @param eList  the e list
     * @param mapper the mapper
     * @return the list
     */
    public static <E, R> List<R> collect(Collection<E> eList, Function<E, R> mapper) {
        if (CollectionUtils.isEmpty(eList)) {
            return new ArrayList<>();
        }
        return eList.stream().map(mapper).collect(Collectors.toList());
    }

    /**
     * Collect set set.
     *
     * @param <E>    the type parameter
     * @param <R>    the type parameter
     * @param eList  the e list
     * @param mapper the mapper
     * @return the set
     */
    public static <E, R> Set<R> collectSet(Collection<E> eList, Function<E, R> mapper) {
        if (CollectionUtils.isEmpty(eList)) {
            return new HashSet<>();
        }
        return eList.stream().map(mapper).collect(Collectors.toSet());
    }

    /**
     * Sorted collect list.
     *
     * @param <E>    the type parameter
     * @param <R>    the type parameter
     * @param eList  the e list
     * @param mapper the mapper
     * @return the list
     */
    public static <E, R> List<R> sortedCollect(Collection<E> eList, Function<E, R> mapper) {
        if (CollectionUtils.isEmpty(eList)) {
            return new ArrayList<>();
        }
        return eList.stream().map(mapper).sorted().collect(Collectors.toList());
    }

    /**
     * To map map.
     *
     * @param <E>    the type parameter
     * @param <R>    the type parameter
     * @param eList  the e list
     * @param mapper the mapper
     * @return the map
     */
    public static <E, R> Map<R, E> toMap(Collection<E> eList, Function<E, R> mapper) {
        if (CollectionUtils.isEmpty(eList)) {
            return new HashMap<>();
        }
        return eList.stream().collect(Collectors.toMap(mapper, e -> e, (p, n) -> n));
    }

    /**
     * To map throws map.
     *
     * @param <E>    the type parameter
     * @param <R>    the type parameter
     * @param eList  the e list
     * @param mapper the mapper
     * @return the map
     */
    public static <E, R> Map<R, E> toMapThrows(Collection<E> eList, Function<E, R> mapper) {
        if (CollectionUtils.isEmpty(eList)) {
            return new HashMap<>();
        }
        return eList.stream().collect(Collectors.toMap(mapper, e -> e));
    }

    /**
     * To kv map map.
     *
     * @param <E>         the type parameter
     * @param <K>         the type parameter
     * @param <V>         the type parameter
     * @param eList       the e list
     * @param keyMapper   the key mapper
     * @param valueMapper the value mapper
     * @return the map
     */
    public static <E, K, V> Map<K, V> toKVMap(Collection<E> eList, Function<E, K> keyMapper, Function<E, V> valueMapper) {
        if (CollectionUtils.isEmpty(eList)) {
            return new HashMap<>();
        }
        return eList.stream().collect(Collectors.toMap(keyMapper, valueMapper, (p, n) -> n));
    }

    /**
     * Reduce r.
     *
     * @param <E>      the type parameter
     * @param <R>      the type parameter
     * @param eList    the e list
     * @param mapper   the mapper
     * @param identity the identity
     * @param operator the operator
     * @return the r
     */
    public static <E, R extends Number> R reduce(Collection<E> eList, Function<E, R> mapper, R identity, BinaryOperator<R> operator) {
        if (CollectionUtils.isEmpty(eList)) {
            return identity;
        }
        return eList.stream().map(mapper).reduce(identity, operator);
    }

    /**
     * 聚合
     *
     * @param <E>    the type parameter
     * @param <R>    the type parameter
     * @param eList  the e list
     * @param mapper the mapper
     * @return list list
     */
    public static <E, R> List<R> gatherList(List<E> eList, Function<E, Stream<R>> mapper) {
        if (CollectionUtils.isEmpty(eList)) {
            return new ArrayList<>();
        }
        return eList.stream().flatMap(mapper).collect(Collectors.toList());
    }

    public static <E, R> Set<R> gatherSet(List<E> eList, Function<E, Stream<R>> mapper) {
        if (CollectionUtils.isEmpty(eList)) {
            return new HashSet<>();
        }
        return eList.stream().flatMap(mapper).collect(Collectors.toSet());
    }

    /**
     * 聚合
     *
     * @param <E>    the type parameter
     * @param eList  the e list
     * @param mapper the mapper
     * @return integer integer
     */
    public static <E> Integer gatherInt(List<E> eList, Function<E, Integer> mapper) {
        if (CollectionUtils.isEmpty(eList)) {
            return 0;
        }
        return eList.stream().map(mapper).reduce(0, Integer::sum);
    }

    /**
     * 聚合
     *
     * @param <E>    the type parameter
     * @param eList  the e list
     * @param mapper the mapper
     * @return big decimal
     */
    public static <E> BigDecimal gatherBigDecimal(List<E> eList, Function<E, BigDecimal> mapper) {
        if (CollectionUtils.isEmpty(eList)) {
            return BigDecimal.ZERO;
        }
        return eList.stream().map(mapper).reduce(BigDecimal.ZERO, BigDecimal::add);
    }

    /**
     * Gather big decimal big decimal.
     *
     * @param <E>    the type parameter
     * @param eSet   the e set
     * @param mapper the mapper
     * @return the big decimal
     */
    public static <E> BigDecimal gatherBigDecimal(Set<E> eSet, Function<E, BigDecimal> mapper) {
        if (CollectionUtils.isEmpty(eSet)) {
            return BigDecimal.ZERO;
        }
        return eSet.stream().map(mapper).reduce(BigDecimal.ZERO, BigDecimal::add);
    }

    /**
     * 分组
     *
     * @param <K>        the type parameter
     * @param <V>        the type parameter
     * @param vList      the v list
     * @param classifier the classifier
     * @return map map
     */
    public static <K, V> Map<K, List<V>> groupBy(List<V> vList, Function<V, K> classifier) {
        if (CollectionUtils.isEmpty(vList)) {
            return new HashMap<>();
        }
        return vList.stream().collect(Collectors.groupingBy(classifier));
    }

    /**
     * Consume if non null.
     *
     * @param <T>       the type parameter
     * @param param     the param
     * @param predicate the predicate
     * @param consumer  the consumer
     */
    public static <T> void consumeIfPredicateTrue(T param, Predicate<T> predicate, Consumer<T> consumer) {
        if (predicate.test(param)) {
            consumer.accept(param);
        }
    }
}
